/*
 ------------------------------------------------------------------------------
	winmad++ -	a test program for using SDL and mad library on Windows
				with object oriented UI style
				with low level mad (seek)

	Copyright (C) 2011 Pierre Veber
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
------------------------------------------------------------------------------
*/

#include "precomp.h"

#define THIS_FILTER		TRACE_MP3

#ifdef MAD_HIGH_LEVEL

#define READ_BUF_SIZE	8192
static unsigned char _read_buf[READ_BUF_SIZE];

void send_message(decode_thread_data_t* pData, int msg)
{
	if (pData->msg == DECODE_MSG_NONE)
	{
		pData->msg = msg;
		while (pData->msg != 0)
		{
			usleep(100000);
		}
	}
}

int get_message(decode_thread_data_t* pData)
{
	int ret = pData->msg;
	pData->msg = DECODE_MSG_NONE;
	return ret;
}

/*
 * This is the input callback. The purpose of this callback is to (re)fill
 * the stream buffer which is to be decoded. In this example, an entire file
 * has been mapped into memory, so we just call mad_stream_buffer() with the
 * address and length of the mapping. When this callback is called a second
 * time, we are finished decoding.
 */

enum mad_flow fMadInputCallback(void *pData, struct mad_stream *pStream)
{
	size_t read, to_read = READ_BUF_SIZE;
	decode_thread_data_t* pDecodeThreadData = (decode_thread_data_t*)pData;
	
	// check for messages
	if (get_message(pDecodeThreadData) == DECODE_MSG_STOP)
	{
		return MAD_FLOW_STOP;
	}
	
	// move remaining frames to beginning of buffer
	if (pStream->next_frame != NULL)
	{
		to_read = pStream->next_frame - _read_buf;
		memmove(_read_buf, pStream->next_frame, READ_BUF_SIZE - to_read);
	}
	read = fread(_read_buf + (READ_BUF_SIZE - to_read), 1, to_read, pDecodeThreadData->fp);
	if (read + READ_BUF_SIZE - to_read > 0)
	{
		mad_stream_buffer(
						pStream,
						(const unsigned char*)_read_buf,
						read + READ_BUF_SIZE - to_read
						);
		if (read != 0)
		{
			return MAD_FLOW_CONTINUE;
		}
	}
	return MAD_FLOW_STOP;
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */

static enum mad_flow fMadOutputCallback(void* pData, struct mad_header const *pHeader, struct mad_pcm *pPcm)
{
	char _buf[4];
	unsigned int nchannels, nsamples;
	mad_fixed_t const *left_ch, *right_ch;
	signed int sample;
	int rc;

	decode_thread_data_t* pDecodeThreadData = (decode_thread_data_t*)pData;

	nchannels = pPcm->channels;
	nsamples  = pPcm->length;

	left_ch   = pPcm->samples[0];
	right_ch  = pPcm->samples[1];
  
	while (nsamples--)
	{
		/* output sample(s) in 16-bit signed little-endian PCM */
		sample = scale(*left_ch++);
		_buf[0] = (sample >> 0) & 0xff;
		_buf[1] = (sample >> 8) & 0xff;
		sample = scale(*right_ch++);
		_buf[2] = (sample >> 0) & 0xff;
		_buf[3] = (sample >> 8) & 0xff;

		rc = waveWriteIn(&(pDecodeThreadData->audio), _buf, 1);
		/*if (rc != 1)
		{
			TRACE( "short write, write %d frames\n", rc);
		}*/
	}
	
	return MAD_FLOW_CONTINUE;
}

/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or
 * libmad/stream.h) header file.
 */

static
enum mad_flow fMadErrorCallback(void* pData, struct mad_stream* pStream, struct mad_frame* pFrame)
{
	TRACE("decoding error 0x%04x (%S)\n", pStream->error, mad_stream_errorstr(pStream));

	/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

	return MAD_FLOW_CONTINUE;
}

int fDecodeThread(void* lpParameter)
{
	int result;
	decode_thread_data_t* pDecodeThreadData = (decode_thread_data_t*)lpParameter;

	pDecodeThreadData->status = DECODE_STATUS_DECODING;

	/* configure input, output, and error functions */
	mad_decoder_init(
		&pDecodeThreadData->decoder,
		pDecodeThreadData,
        fMadInputCallback,
        0 /* header */,
        0 /* filter */,
        fMadOutputCallback,
        fMadErrorCallback,
        0 /* message */
        );

	/* start decoding */
	result = mad_decoder_run(&pDecodeThreadData->decoder, MAD_DECODER_MODE_SYNC);

	/* release the decoder */
	mad_decoder_finish(&pDecodeThreadData->decoder);
	
	/* change status */
	pDecodeThreadData->status = DECODE_STATUS_FINISHED;

	return 0;
}

decode_thread_data_t* madDecode(audio_data_t* pAudio, const char* pszFile)
{
	decode_thread_data_t* pDecodeThreadData;
	
	pDecodeThreadData = (decode_thread_data_t*)malloc(sizeof(decode_thread_data_t));
	memset(pDecodeThreadData, 0, sizeof(decode_thread_data_t));

	pDecodeThreadData->fp = NULL;
	pDecodeThreadData->szFilePath = _strdup(pszFile);
	memcpy(&(pDecodeThreadData->audio), pAudio, sizeof(audio_data_t));

	/* open the file for reading */
	pDecodeThreadData->fp = fopen(pszFile, "rb");
	if (pDecodeThreadData->fp == NULL)
	{
		TRACE("Unable to fd open: %S !\n", pszFile);
		goto cleanup;
	}

	/* prepare audio */
	waveReset(&(pDecodeThreadData->audio));

	/* init of decode thread */
	pDecodeThreadData->tDecodeThread = SDL_CreateThread(fDecodeThread, pDecodeThreadData);
	if (pDecodeThreadData->tDecodeThread == NULL)
	{
		TRACE("Error creating decode thread!\n");
		goto cleanup;
	}
	
cleanup:
	return pDecodeThreadData;
}

int madStop(decode_thread_data_t* pDecodeThreadData)
{
	int iDecodeThreadExit;

	/* stop decode thread */
	if ( pDecodeThreadData->status == DECODE_STATUS_DECODING )
	{
		TRACE("sending message for thread stop...\n");
		send_message(pDecodeThreadData, DECODE_MSG_STOP);
	}

	TRACE("waiting for Decode thread to exit...\n");
	SDL_WaitThread(pDecodeThreadData->tDecodeThread, &iDecodeThreadExit);
	if (iDecodeThreadExit != 0)
	{
		TRACE("Decode thread return value\n");
	}
	else
	{
		TRACE("Decode thread was cancelled.\n");
	}

	/* 
		_TO_DO
		decode data cleanup
	*/
	if (pDecodeThreadData->fp != NULL)
	{
		fclose(pDecodeThreadData->fp);
	}

	return 0;
}

#else
/* MAD_HIGH_LEVEL */

/* This table represents the subband-domain filter characteristics. It
 * is initialized by the ParseArgs() function and is used as
 * coefficients against each subband samples when DoFilter is non-nul.
 */
mad_fixed_t	Filter[32];

/* DoFilter is non-nul when the Filter table defines a filter bank to
 * be applied to the decoded audio subbands.
 */
int			DoFilter=0;

const char	*ProgName = "sdlmad++";


/****************************************************************************
 * Return an error string associated with a mad error code.					*
 ****************************************************************************/
#define MadErrorString(x) mad_stream_errorstr(x)

/****************************************************************************
 * Print human readable informations about an audio MPEG frame.				*
 ****************************************************************************/
struct xing
{
	long flags;
	unsigned long frames;
	unsigned long bytes;
	unsigned char toc[100];
	long scale;
};

enum
{
	XING_FRAMES = 0x00000001L,
	XING_BYTES  = 0x00000002L,
	XING_TOC    = 0x00000004L,
	XING_SCALE  = 0x00000008L
};

# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')

/*
 * NAME:  parse_xing()
 * DESCRIPTION: read a Xing VBR tag
 */
static
int parse_xing(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
{
	if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
		goto fail;
		
	xing->flags = mad_bit_read(&ptr, 32);
	
	bitlen -= 64;
	
	if (xing->flags & XING_FRAMES)
	{
		if (bitlen < 32)
			goto fail;
			
		xing->frames = mad_bit_read(&ptr, 32);
		
		bitlen -= 32;
	}
	
	if (xing->flags & XING_BYTES)
	{
		if (bitlen < 32)
			goto fail;
			
		xing->bytes = mad_bit_read(&ptr, 32);
		
		bitlen -= 32;
	}
	
	if (xing->flags & XING_TOC)
	{
		int i;
		
		if (bitlen < 800)
			goto fail;
			
		for (i = 0; i < 100; ++i)
			xing->toc[i] = (unsigned char) mad_bit_read(&ptr, 8);
			
		bitlen -= 800;
	}
	
	if (xing->flags & XING_SCALE)
	{
		if (bitlen < 32)
			goto fail;
			
		xing->scale = mad_bit_read(&ptr, 32);
		
		bitlen -= 32;
	}
	
	return 0;
	
fail:
	xing->flags = 0;
	return -1;
}

static int PrintFrameInfo(FILE *fp, struct mad_header *Header)
{
	const char	*Layer,
				*Mode,
				*Emphasis;

	/* Convert the layer number to it's printed representation. */
	switch(Header->layer)
	{
		case MAD_LAYER_I:
			Layer="I";
			break;
		case MAD_LAYER_II:
			Layer="II";
			break;
		case MAD_LAYER_III:
			Layer="III";
			break;
		default:
			Layer="(unexpected layer value)";
			break;
	}

	/* Convert the audio mode to it's printed representation. */
	switch(Header->mode)
	{
		case MAD_MODE_SINGLE_CHANNEL:
			Mode="single channel";
			break;
		case MAD_MODE_DUAL_CHANNEL:
			Mode="dual channel";
			break;
		case MAD_MODE_JOINT_STEREO:
			Mode="joint (MS/intensity) stereo";
			break;
		case MAD_MODE_STEREO:
			Mode="normal LR stereo";
			break;
		default:
			Mode="(unexpected mode value)";
			break;
	}

	/* Convert the emphasis to it's printed representation. Note that
	 * the MAD_EMPHASIS_RESERVED enumeration value appeared in libmad
	 * version 0.15.0b.
	 */
	switch(Header->emphasis)
	{
		case MAD_EMPHASIS_NONE:
			Emphasis="no";
			break;
		case MAD_EMPHASIS_50_15_US:
			Emphasis="50/15 us";
			break;
		case MAD_EMPHASIS_CCITT_J_17:
			Emphasis="CCITT J.17";
			break;
#if (MAD_VERSION_MAJOR>=1) || \
	((MAD_VERSION_MAJOR==0) && (MAD_VERSION_MINOR>=15))
		case MAD_EMPHASIS_RESERVED:
			Emphasis="reserved(!)";
			break;
#endif
		default:
			Emphasis="(unexpected emphasis value)";
			break;
	}

	TRACE("%S: %lu kb/s audio MPEG layer %S stream %S CRC, %S with %S emphasis at %d Hz sample rate\n",
			ProgName,Header->bitrate,Layer,
			Header->flags&MAD_FLAG_PROTECTION?"with":"without",
			Mode,Emphasis,Header->samplerate);
	return(ferror(fp));
}

/****************************************************************************
 * Applies a frequency-domain filter to audio data in the subband-domain.	*
 ****************************************************************************/
static void ApplyFilter(struct mad_frame *Frame)
{
	int	Channel,
		Sample,
		Samples,
		SubBand;

	/* There is two application loops, each optimized for the number
	 * of audio channels to process. The first alternative is for
	 * two-channel frames, the second is for mono-audio.
	 */
	Samples=MAD_NSBSAMPLES(&Frame->header);
	if(Frame->header.mode!=MAD_MODE_SINGLE_CHANNEL)
		for(Channel=0;Channel<2;Channel++)
			for(Sample=0;Sample<Samples;Sample++)
				for(SubBand=0;SubBand<32;SubBand++)
					Frame->sbsample[Channel][Sample][SubBand]=
						mad_f_mul(Frame->sbsample[Channel][Sample][SubBand],
								  Filter[SubBand]);
	else
		for(Sample=0;Sample<Samples;Sample++)
			for(SubBand=0;SubBand<32;SubBand++)
				Frame->sbsample[0][Sample][SubBand]=
					mad_f_mul(Frame->sbsample[0][Sample][SubBand],
							  Filter[SubBand]);
}

/****************************************************************************
 * Main decoding loop. This is where mad is used.							*
 ****************************************************************************/

#define INPUT_BUFFER_SIZE	8192

int seek;

static int MpegAudioDecoder(void* lpParameter)
{
	struct mad_stream	Stream;
	struct mad_frame	Frame;
	struct mad_synth	Synth;
	mad_timer_t			Timer;
	mad_timer_t			Length;
	size_t				Size;
	unsigned char		InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD],
						*GuardPtr=NULL;
	char				OutputBuffer[4];
	int					Status=0,
						i,
						fr_dec_seek = 0,
						rc;
	unsigned long		FrameCount=0;
	
	decode_thread_data_t* pDecodeThreadData = (decode_thread_data_t*)lpParameter;

	seek = 0;

	pDecodeThreadData->status = DECODE_STATUS_DECODING;

	/* First the structures used by libmad must be initialized. */
	mad_stream_init(&Stream);
	mad_frame_init(&Frame);
	mad_synth_init(&Synth);
	mad_timer_reset(&Timer);
	mad_timer_reset(&Length);

	/* Decoding options can here be set in the options field of the
	 * Stream structure.
	 */

	/* {1} When decoding from a file we need to know when the end of
	 * the file is reached at the same time as the last bytes are read
	 * (see also the comment marked {3} bellow). Neither the standard
	 * C fread() function nor the POSIX read() system call provides
	 * this feature. We thus need to perform our reads through an
	 * interface having this feature, this is implemented here by the
	 * bstdfile.c module.

	BstdFile=NewBstdFile(InputFp);
	if(BstdFile==NULL)
	{
		TRACE("%S: can't create a new bstdfile_t (%S).\n",
				ProgName,strerror(errno));
		return(1);
	}
	 */
	fseek(pDecodeThreadData->fp , 0, SEEK_END);
	Size = ftell(pDecodeThreadData->fp);
	fseek(pDecodeThreadData->fp, 0, SEEK_SET);

	/* This is the decoding loop. */
	do
	{
		/* The input bucket must be filled if it becomes empty or if
		 * it's the first execution of the loop.
		 */
		if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN)
		{
			size_t			ReadSize,
							Remaining;
			unsigned char	*ReadStart;

			/* {2} libmad may not consume all bytes of the input
			 * buffer. If the last frame in the buffer is not wholly
			 * contained by it, then that frame's start is pointed by
			 * the next_frame member of the Stream structure. This
			 * common situation occurs when mad_frame_decode() fails,
			 * sets the stream error code to MAD_ERROR_BUFLEN, and
			 * sets the next_frame pointer to a non NULL value. (See
			 * also the comment marked {4} bellow.)
			 *
			 * When this occurs, the remaining unused bytes must be
			 * put back at the beginning of the buffer and taken in
			 * account before refilling the buffer. This means that
			 * the input buffer must be large enough to hold a whole
			 * frame at the highest observable bit-rate (currently 448
			 * kb/s). XXX=XXX Is 2016 bytes the size of the largest
			 * frame? (448000*(1152/32000))/8
			 */
			if(Stream.next_frame!=NULL)
			{
				Remaining=Stream.bufend-Stream.next_frame;
				memmove(InputBuffer,Stream.next_frame,Remaining);
				ReadStart=InputBuffer+Remaining;
				ReadSize=INPUT_BUFFER_SIZE-Remaining;
			}
			else
				ReadSize=INPUT_BUFFER_SIZE,
					ReadStart=InputBuffer,
					Remaining=0;

			/* Fill-in the buffer. If an error occurs print a message
			 * and leave the decoding loop. If the end of stream is
			 * reached we also leave the loop but the return status is
			 * left untouched.
			 */
			ReadSize=fread(ReadStart,1,ReadSize,pDecodeThreadData->fp);
			if ( ReadSize <= 0 )
			{
				if(ferror(pDecodeThreadData->fp))
				{
					TRACE("%S: read error on bit-stream (%S)\n",
							ProgName,strerror(errno));
					Status=1;
				}
				if(feof(pDecodeThreadData->fp))
				{
					TRACE("%S: end of input stream\n",ProgName);
				}
				break;
			}

			/* {3} When decoding the last frame of a file, it must be
			 * followed by MAD_BUFFER_GUARD zero bytes if one wants to
			 * decode that last frame. When the end of file is
			 * detected we append that quantity of bytes at the end of
			 * the available data. Note that the buffer can't overflow
			 * as the guard size was allocated but not used the the
			 * buffer management code. (See also the comment marked
			 * {1}.)
			 *
			 * In a message to the mad-dev mailing list on May 29th,
			 * 2001, Rob Leslie explains the guard zone as follows:
			 *
			 *    "The reason for MAD_BUFFER_GUARD has to do with the
			 *    way decoding is performed. In Layer III, Huffman
			 *    decoding may inadvertently read a few bytes beyond
			 *    the end of the buffer in the case of certain invalid
			 *    input. This is not detected until after the fact. To
			 *    prevent this from causing problems, and also to
			 *    ensure the next frame's main_data_begin pointer is
			 *    always accessible, MAD requires MAD_BUFFER_GUARD
			 *    (currently 8) bytes to be present in the buffer past
			 *    the end of the current frame in order to decode the
			 *    frame."
			if(BstdFileEofP(BstdFile))
			{
				GuardPtr=ReadStart+ReadSize;
				memset(GuardPtr,0,MAD_BUFFER_GUARD);
				ReadSize+=MAD_BUFFER_GUARD;
			}
			 */

			/* Pipe the new buffer content to libmad's stream decoder
             * facility.
			 */
			mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining);
			Stream.error=(mad_error)0;
		}

		// check for messages
		int msg = get_message(pDecodeThreadData);
		if (msg == DECODE_MSG_PAUSE)
		{
			do
			{
				usleep(100000);
				msg = get_message(pDecodeThreadData);
			}
			while (msg == DECODE_MSG_NONE);
		}
		if (msg == DECODE_MSG_STOP)
		{
			TRACE("%S: Stop message received.\n", ProgName);
			break;
		}
		if (msg == DECODE_MSG_SEEK)
		{
			double fraction;
			unsigned long position;
			
			seek += 20;
			fraction = (double)seek / 100.0;
			position = (unsigned long)(mad_timer_count(Length, MAD_UNITS_MILLISECONDS) * fraction);
			mad_timer_set(&Timer, position / 1000, position % 1000, 1000);
			fseek(pDecodeThreadData->fp, (long)(Size * fraction), SEEK_SET);
			size_t ReadSize = fread(InputBuffer,1,INPUT_BUFFER_SIZE,pDecodeThreadData->fp);
			if ( ReadSize <= 0 )
			{
				if(ferror(pDecodeThreadData->fp))
				{
					TRACE("%S: read error on bit-stream (%S)\n",
							ProgName,strerror(errno));
					Status=1;
				}
				if(feof(pDecodeThreadData->fp))
				{
					TRACE("%S: end of input stream\n",ProgName);
				}
				break;
			}
			mad_stream_buffer(&Stream,InputBuffer,ReadSize);
			mad_frame_mute(&Frame);	
			mad_synth_mute(&Synth);
			fr_dec_seek = 4;
		}
		
		/* Decode the next MPEG frame. The streams is read from the
		 * buffer, its constituents are break down and stored the the
		 * Frame structure, ready for examination/alteration or PCM
		 * synthesis. Decoding options are carried in the Frame
		 * structure from the Stream structure.
		 *
		 * Error handling: mad_frame_decode() returns a non zero value
		 * when an error occurs. The error condition can be checked in
		 * the error member of the Stream structure. A mad error is
		 * recoverable or fatal, the error status is checked with the
		 * MAD_RECOVERABLE macro.
		 *
		 * {4} When a fatal error is encountered all decoding
		 * activities shall be stopped, except when a MAD_ERROR_BUFLEN
		 * is signaled. This condition means that the
		 * mad_frame_decode() function needs more input to complete
		 * its work. One shall refill the buffer and repeat the
		 * mad_frame_decode() call. Some bytes may be left unused at
		 * the end of the buffer if those bytes forms an incomplete
		 * frame. Before refilling, the remaining bytes must be moved
		 * to the beginning of the buffer and used for input for the
		 * next mad_frame_decode() invocation. (See the comments
		 * marked {2} earlier for more details.)
		 *
		 * Recoverable errors are caused by malformed bit-streams, in
		 * this case one can call again mad_frame_decode() in order to
		 * skip the faulty part and re-sync to the next frame.
		 */
		if(mad_frame_decode(&Frame,&Stream))
		{
			if(MAD_RECOVERABLE(Stream.error))
			{
				/* Do not print a message if the error is a loss of
				 * synchronization and this loss is due to the end of
				 * stream guard bytes. (See the comments marked {3}
				 * supra for more informations about guard bytes.)
				 */
				if(Stream.error!=MAD_ERROR_LOSTSYNC ||
				   Stream.this_frame!=GuardPtr)
				{
					TRACE("%S: recoverable frame level error (%S)\n",
							ProgName,MadErrorString(&Stream));
					fflush(stderr);
				}
				continue;
			}
			else
				if(Stream.error==MAD_ERROR_BUFLEN)
					continue;
				else
				{
					TRACE("%S: unrecoverable frame level error (%S).\n",
							ProgName,MadErrorString(&Stream));
					Status=1;
					break;
				}
		}
		else
		{
			if (fr_dec_seek > 0)
			{
				fr_dec_seek--;
			}
		}

		/* The characteristics of the stream's first frame is printed
		 * on stderr. The first frame is representative of the entire
		 * stream.
		 */
		if (FrameCount==0)
		{
			mad_timer_set(&Length, 0, 1, Frame.header.bitrate / 8);
			mad_timer_multiply(&Length, Size);
			if(PrintFrameInfo(stderr,&Frame.header))
			{
				Status=1;
				break;
			}
		}

		/* Accounting. The computed frame duration is in the frame
		 * header structure. It is expressed as a fixed point number
		 * whole data type is mad_timer_t. It is different from the
		 * samples fixed point format and unlike it, it can't directly
		 * be added or subtracted. The timer module provides several
		 * functions to operate on such numbers. Be careful there, as
		 * some functions of libmad's timer module receive some of
		 * their mad_timer_t arguments by value!
		 */
		FrameCount++;
		mad_timer_add(&Timer,Frame.header.duration);

		/* Between the frame decoding and samples synthesis we can
		 * perform some operations on the audio data. We do this only
		 * if some processing was required. Detailed explanations are
		 * given in the ApplyFilter() function.
		 */
		if(DoFilter)
			ApplyFilter(&Frame);

		/* Once decoded the frame is synthesized to PCM samples. No errors
		 * are reported by mad_synth_frame();
		 */
		if (fr_dec_seek <= 1)
		{
			mad_synth_frame(&Synth,&Frame);
		}

		/* Synthesized samples must be converted from libmad's fixed
		 * point number to the consumer format. Here we use unsigned
		 * 16 bit big endian integers on two channels. Integer samples
		 * are temporarily stored in a buffer that is flushed when
		 * full.
		 */
		for(i=0;(i<Synth.pcm.length) && (fr_dec_seek==0);i++)
		{
			signed int	Sample;

			/* Left channel */
			Sample=scale(Synth.pcm.samples[0][i]);
			OutputBuffer[0] = (Sample >> 0) & 0xff;
			OutputBuffer[1] = (Sample >> 8) & 0xff;

			/* Right channel. If the decoded stream is monophonic then
			 * the right output channel is the same as the left one.
			 */
			if(MAD_NCHANNELS(&Frame.header)==2)
				Sample=scale(Synth.pcm.samples[1][i]);
			OutputBuffer[2] = (Sample >> 0) & 0xff;
			OutputBuffer[3] = (Sample >> 8) & 0xff;

			rc = waveWriteIn(&(pDecodeThreadData->audio), OutputBuffer, 1);
		}
	
	} while (1);

	/* The input file was completely read; the memory allocated by our
	 * reading module must be reclaimed.
	BstdFileDestroy(BstdFile);
	 */

	/* Mad is no longer used, the structures that were initialized must
     * now be cleared.
	 */
	mad_synth_finish(&Synth);
	mad_frame_finish(&Frame);
	mad_stream_finish(&Stream);

	/* Accounting report if no error occurred. */
	if(!Status)
	{
		char	Buffer[80];

		/* The duration timer is converted to a human readable string
		 * with the versatile, but still constrained mad_timer_string()
		 * function, in a fashion not unlike strftime(). The main
		 * difference is that the timer is broken into several
		 * values according some of it's arguments. The units and
		 * fracunits arguments specify the intended conversion to be
		 * executed.
		 *
		 * The conversion unit (MAD_UNIT_MINUTES in our example) also
		 * specify the order and kind of conversion specifications
		 * that can be used in the format string.
		 *
		 * It is best to examine libmad's timer.c source-code for details
		 * of the available units, fraction of units, their meanings,
		 * the format arguments, etc.
		 */
		mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u",
						 MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0);
		TRACE("%S: %lu frames decoded (%S).\n",
				ProgName,FrameCount,Buffer);
	}

	pDecodeThreadData->status = DECODE_STATUS_FINISHED;

	/* That's the end of the world (in the H. G. Wells way). */
	return(Status);
}

/*
===============================================================================	
	
	LEGACY CODE

===============================================================================
*/

void send_message(decode_thread_data_t* pData, int msg)
{
	if (pData->msg == DECODE_MSG_NONE)
	{
		pData->msg = msg;
		while (pData->msg != 0)
		{
			usleep(100000);
		}
	}
}

int get_message(decode_thread_data_t* pData)
{
	int ret = pData->msg;
	pData->msg = DECODE_MSG_NONE;
	return ret;
}

decode_thread_data_t* madDecode(audio_data_t* pAudio, const char* pszFile)
{
	decode_thread_data_t* pDecodeThreadData;
	
	pDecodeThreadData = (decode_thread_data_t*)malloc(sizeof(decode_thread_data_t));
	memset(pDecodeThreadData, 0, sizeof(decode_thread_data_t));

	pDecodeThreadData->fp = NULL;
	pDecodeThreadData->szFilePath = _strdup(pszFile);
	memcpy(&(pDecodeThreadData->audio), pAudio, sizeof(audio_data_t));

	/* open the file for reading */
	pDecodeThreadData->fp = fopen(pszFile, "rb");
	if (pDecodeThreadData->fp == NULL)
	{
		TRACE("Unable to fd open: %S !\n", pszFile);
		goto cleanup;
	}

	/* prepare audio */
	waveReset(&(pDecodeThreadData->audio));

	/* init of decode thread */
	pDecodeThreadData->tDecodeThread = SDL_CreateThread(MpegAudioDecoder, pDecodeThreadData);
	if (pDecodeThreadData->tDecodeThread == NULL)
	{
		TRACE("Error creating decode thread!\n");
		goto cleanup;
	}
	
cleanup:
	return pDecodeThreadData;
}

int madStop(decode_thread_data_t* pDecodeThreadData)
{
	int iDecodeThreadExit;

	/* stop decode thread */
	if ( pDecodeThreadData->status == DECODE_STATUS_DECODING )
	{
		TRACE("sending message for thread stop...\n");
		send_message(pDecodeThreadData, DECODE_MSG_STOP);
	}

	TRACE("waiting for Decode thread to exit...\n");
	SDL_WaitThread(pDecodeThreadData->tDecodeThread, &iDecodeThreadExit);
	if (iDecodeThreadExit != 0)
	{
		TRACE("Decode thread return value\n");
	}
	else
	{
		TRACE("Decode thread was cancelled.\n");
	}

	/* 
		_TO_DO
		decode data cleanup
	*/
	if (pDecodeThreadData->fp != NULL)
	{
		fclose(pDecodeThreadData->fp);
	}

	return 0;
}

void madSeek(decode_thread_data_t* pDecodeThreadData)
{
	if ( pDecodeThreadData->status == DECODE_STATUS_DECODING )
	{
		TRACE("sending message for thread seek...\n");
		send_message(pDecodeThreadData, DECODE_MSG_SEEK);
	}
}

void madPause(decode_thread_data_t* pDecodeThreadData)
{
	if ( pDecodeThreadData->status == DECODE_STATUS_DECODING )
	{
		TRACE("sending message for thread pause...\n");
		send_message(pDecodeThreadData, DECODE_MSG_PAUSE);
	}
}

#endif /* MAD_HIGH_LEVEL */
